package com.liuyu.common.framework.security.web;

import cn.hutool.http.HttpUtil;
import com.google.common.collect.Maps;
import com.liuyu.common.framework.lang.exception.BusinessException;
import com.liuyu.common.framework.security.oauth.OauthProperties;
import com.liuyu.common.framework.security.vo.RefreshToken;
import com.liuyu.common.framework.web.response.ApiResponse;
import com.liuyu.common.framework.web.swagger3.annotation.ApiVersion;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @ClassName OauthSecurityController
 * @Description: 处理oauth2 的相应
 * @Author: liuyu
 * @Date: Created in 2020/4/16 20:28
 * @Version: 1
 * @Modified By:
 */
@Controller
@RefreshScope
@Slf4j
@Tag(name = "前端-oauth2认证后处理", description = "前端-oauth2认证后处理")
@EnableConfigurationProperties(value = OauthProperties.class)
public class OauthSecurityController {
    private static final String ERROR = "error";
    private static final String REDIRECT_URI = "redirect_uri";
    private static final String ORIGIN = "origin";
    private static final String X_FORWARDED_PREFIX = "x-forwarded-prefix";
    private static final String STRING = "/";
    private static final String DEFAULT = "default";
    @Autowired
    private OauthProperties oauthProperties;


    @ApiVersion("front")
    @Operation(summary = "认证成功后的处理")
    @RequestMapping(value = "/oauth/success", method = {RequestMethod.GET, RequestMethod.POST})
    public String getCode(@RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state) {
        log.info("认证成功：code:{},state:{}", code, state);
        String[] ss = state.split("@");
        if (ss.length == 1) {
            throw new BusinessException("state 格式不正确!");
        }
        if (!checkLastRedirectUri(ss[0], ss[1])) {
            throw new BusinessException("state 跳转不合法!");
        }

        String url = ss[0].replace("~", "#") + (state.contains("?") ? "&code=" + code : "?code=" + code);
        return "redirect:" + url;
    }


    @ApiVersion("front")
    @Operation(summary = "根据code获取token")
    @PostMapping(value = "/oauth/get/token")
    @ResponseBody
    public ApiResponse<String> getToken(@RequestParam("code") String code, @RequestParam("client_id") String clientId, HttpServletRequest request) {
        Map<String, Object> parameterMap = Maps.newHashMapWithExpectedSize(4);
        parameterMap.put("client_id", clientId);
        parameterMap.put("client_secret", oauthProperties.getKeyPairMap().get(clientId));
        parameterMap.put("grant_type", "authorization_code");
        parameterMap.put("code", code);
        String redirectUri = getRedirectUri(clientId, request);
        log.info("redirect_uri:{}", redirectUri);
        parameterMap.put(REDIRECT_URI, redirectUri);
        String token = HttpUtil.post(oauthProperties.getOauthTokenUrl(), parameterMap);
        if (log.isDebugEnabled()) {
            log.debug("code get token: client_id:{},code:{},redirect_uri:{},token:{}", clientId, code, redirectUri, token);
        }
        if (token.contains(ERROR)) {
            log.error("换取token失败: client_id:{},code:{},redirect_uri:{},token:{}", clientId, code, redirectUri, token);
            return ApiResponse.customize("401", "code非法,请重新登陆", token);
        }
        return ApiResponse.success("", token);
    }

    private String getRedirectUri(String clientId, HttpServletRequest request) {
        if (oauthProperties.isAutoAdaptiveRedirectUri()) {
            String origin = request.getHeader(ORIGIN);
            if (StringUtils.isNotBlank(origin)) {
                String contextPath = request.getHeader(X_FORWARDED_PREFIX);
                String servletPath;
                if (oauthProperties.getAppRedirectServletPath().containsKey(clientId)) {
                    servletPath = oauthProperties.getAppRedirectServletPath().get(clientId);
                } else {
                    servletPath = oauthProperties.getAppRedirectServletPath().get(DEFAULT);
                }
                String redirectUri = origin + contextPath + servletPath;
                return redirectUri;
            }
        }
        if (oauthProperties.getAppRedirectUrl().containsKey(clientId)) {
            return oauthProperties.getAppRedirectUrl().get(clientId);
        }
        return null;
    }


    public boolean checkLastRedirectUri(String requestUrl, String clientId) {
        Map<String, List<String>> map = oauthProperties.getAppLastRedirectUrl();
        boolean hasPower = false;
        if (map.containsKey(clientId)) {
            List<String> urls = map.get(clientId);
            for (String uri : urls) {
                PathMatcher matcher = new AntPathMatcher();
                if (matcher.match(uri, requestUrl)) {
                    hasPower = true;
                    break;
                }
            }
        }
        return hasPower;
    }


    @ApiVersion("front")
    @Operation(summary = "根据code获取token")
    @PostMapping("/oauth/refresh/token")
    @ResponseBody
    public ApiResponse<String> refreshToken(@Valid @RequestBody RefreshToken refreshToken) {
        Map<String, Object> parameterMap = Maps.newHashMapWithExpectedSize(4);
        parameterMap.put("client_id", refreshToken.getClientId());
        parameterMap.put("client_secret", oauthProperties.getKeyPairMap().get(refreshToken.getClientId()));
        parameterMap.put("grant_type", "refresh_token");
        parameterMap.put("refresh_token", refreshToken.getRefreshToken());
        String token = HttpUtil.post(oauthProperties.getOauthTokenUrl(), parameterMap);
        if (log.isDebugEnabled()) {
            log.debug("refresh token: client_id:{},token:{}", refreshToken.getClientId(), token);
        }
        if (token.contains(ERROR)) {
            return ApiResponse.customize("401", "JWT非法,请重新登陆", token);
        }
        return ApiResponse.success("", token);
    }


}
